﻿#pragma once

#include <queue>
#include <array>
#include <memory>
#include <mutex>
#include <utility>

/**
 * @brief  固定数量对象池
 * @tparam T 对象类型，不建议使用指针类型
 * @tparam max 对象池最大数量
 */
template<class T, std::size_t max>
class ObjectPool
{
    using object = std::shared_ptr<T>;
public:
    template<class... Args>
    explicit ObjectPool(Args &&...args)
    {
        for (std::size_t i = 0; i < max; ++i)
        {
            m_objs[i] = std::make_shared<T>(std::forward<Args>(args)...);
            m_freeObjectIdxQueue.push(i);
        }
    }

    /**
     * @brief 对象池容量
     * @return 对象池容量
     */
    constexpr auto size() -> std::size_t
    {
        return max;
    }

    /**
     * @brief 对象池容量
     * @return 对象池容量
     */
    constexpr auto capacity() -> std::size_t
    {
        return max;
    }

    /**
     * @brief 剩余容量
     * @return 剩余容量 0~max
     */
    auto remaining() -> std::size_t
    {
        std::lock_guard<std::mutex> locker{m_mtx};
        return m_freeObjectIdxQueue.size();
    }

    /**
     * @brief 对象池是否可用
     * @return 对象池是否可用
     */
    auto available() -> bool
    {
        return !m_freeObjectIdxQueue.empty();
    }

    /**
     * @brief 获取池子中的一个对象
     * @return object
     * @note  有借有还，再借不难; acquire 必须搭配 restore使用。若不归还，会造成对象池剩余空间越来越小
     */
    auto acquire() -> object
    {
        std::lock_guard<std::mutex> locker{m_mtx};
        if (m_freeObjectIdxQueue.empty())
        {
            return nullptr;
        }

        auto id = m_freeObjectIdxQueue.front();
        m_freeObjectIdxQueue.pop();
        return m_objs[id];
    }

    /**
     * @brief 归还从池子中取走的对象
     * @param obj 被归还的对象
     * @note 有借有还，再借不难; restore 必须搭配 acquire。若不归还，会造成对象池剩余空间越来越小
     */
    auto restore(const object &obj) -> void
    {
        if (!obj)
        {
            return;
        }
        std::lock_guard<std::mutex> locker{m_mtx};
        auto iter = std::find(m_objs.cbegin(), m_objs.cend(), obj);
        if (iter == m_objs.end())
        {
            throw std::logic_error{"can not find the object in pool"};
        }
        m_freeObjectIdxQueue.push(iter - m_objs.begin());
    }

    /**
     * @brief 归还从池子中取走的对象
     * @param obj
     * @note 有借有还，再借不难; restore 必须搭配 acquire。若不归还，会造成对象池剩余空间越来越小
     */
    auto restore(object &&obj) -> void
    {
        if (!obj)
        {
            return;
        }
        std::lock_guard<std::mutex> locker{m_mtx};
        auto iter = std::find(m_objs.cbegin(), m_objs.cend(), obj);
        if (iter == m_objs.end())
        {
            throw std::logic_error{"can not find the object in pool"};
        }
        m_freeObjectIdxQueue.push(iter - m_objs.begin());
    }

private:
    std::mutex m_mtx;
    std::array<object, max> m_objs;
    std::queue<std::size_t> m_freeObjectIdxQueue;
};



